"""
@Author    : ghenyar
@Time      : 2025/8/28 13:37
@File      : jwt
@Desc      : 
"""
from datetime import timedelta, datetime, timezone
from typing import Optional, Union, Dict, Any
from jose import JWTError, jwt, ExpiredSignatureError

from app.core.config import conf


class JWTUtils:

    @classmethod
    def generate_token(cls, data: dict, expires_delta: Optional[timedelta] = None) -> str:
        """
        生成令牌
        :param data: 生成令牌的参数
        :param expires_delta: 令牌过期时间
        :return: 生成的令牌
        """
        to_encode = data.copy()
        if expires_delta:
            expire = datetime.now(timezone.utc) + expires_delta
        else:
            expire = datetime.now(timezone.utc) + timedelta(minutes=conf.jwt.expires)
        # exp 必须是 int 类型的时间戳
        to_encode.update({"exp": int(expire.timestamp())})
        encoded_jwt = jwt.encode(to_encode, conf.jwt.secret, algorithm=conf.jwt.algorithm)
        return encoded_jwt

    @classmethod
    def decode_token(cls, token: str) -> Union[Dict[str, Any], bool]:
        """
        解析令牌
        :param token: 令牌
        :return: 解析后的数据或 False（如果解析失败）
        """
        try:
            payload = jwt.decode(token, conf.jwt.secret, algorithms=[conf.jwt.algorithm])
            return payload
        except ExpiredSignatureError:
            # 令牌过期
            return False
        except JWTError:
            # 其他解析错误
            return False

    @classmethod
    def token_expired(cls, token: str) -> bool:
        """
        检查令牌是否已过期
        :param token: 令牌
        :return: True 如果令牌已过期，否则 False
        """
        try:
            jwt.decode(token, conf.jwt.secret, algorithms=[conf.jwt.algorithm])
            return False
        except ExpiredSignatureError:
            return True
        except JWTError:
            return True

    @classmethod
    def refresh_token(cls, token: str, expires_delta: Optional[timedelta] = None) -> Union[str, bool]:
        """
        刷新令牌
        :param token: 原令牌
        :param expires_delta: 过期时间
        :return: 新令牌或 False（如果原令牌无效）
        """
        payload = cls.decode_token(token)
        if not payload:
            return False
        # 移除 exp 字段
        payload.pop("exp", None)
        return cls.generate_token(payload, expires_delta)

    @classmethod
    def get_token_expired(cls, token: str) -> Optional[int]:
        """
        获取令牌过期时间
        :param token: 令牌
        :return: 令牌的过期时间戳
        """
        try:
            payload = jwt.decode(token, conf.jwt.secret, algorithms=[conf.jwt.algorithm], options={"verify_exp": False})
            return payload.get("exp")
        except JWTError:
            return None
